﻿using System;
using System.Data.SqlTypes;

namespace CodeContrib
{
	public class RandomDateTime : IRandom<DateTime>
	{
		private readonly DateTime _maximumValue;
		private readonly DateTime _minimumValue;

		public DateTime MaximumValue
		{
			get { return _maximumValue; }
		}

		public DateTime MinimumValue
		{
			get { return _minimumValue; }
		}

		public RandomDateTime() : this(SqlDateTime.MinValue.Value, SqlDateTime.MaxValue.Value)
		{
		}

		public RandomDateTime(DateTime maxValue) : this(SqlDateTime.MinValue.Value, maxValue)
		{
		}

		public RandomDateTime(DateTime minValue, DateTime maxValue)
		{
			if (minValue > maxValue)
			{
				throw new ArgumentOutOfRangeException("minValue", minValue, "'minValue' cannot be greater than 'maxValue'");
			}

			_maximumValue = maxValue;
			_minimumValue = minValue;
		}

		public DateTime Next()
		{
			var year = new RandomInteger(_minimumValue.Year, _maximumValue.Year + 1).Next();
			var minMonth = 1;
			var maxMonth = 12;

			if (year == _minimumValue.Year)
			{
				minMonth = _minimumValue.Month;
			}

			if (year == _maximumValue.Year)
			{
				maxMonth = _maximumValue.Month;
			}

			var month = new RandomInteger(minMonth, maxMonth + 1).Next();

			var minDay = 1;
			var maxDay = DateTime.DaysInMonth(year, month);

			if (year == _minimumValue.Year
			    && month == _minimumValue.Month)
			{
				minDay = _minimumValue.Day;
			}

			if (year == _maximumValue.Year
			    && month == _maximumValue.Month)
			{
				maxDay = _maximumValue.Day;
			}

			var day = new RandomInteger(minDay, maxDay + 1).Next();

			var minHour = 0;
			var maxHour = 23;

			if (year == _minimumValue.Year && month == _minimumValue.Month
			    && day == _minimumValue.Day)
			{
				minHour = _minimumValue.Hour;
			}

			if (year == _maximumValue.Year && month == _maximumValue.Month
			    && day == _maximumValue.Day)
			{
				maxHour = _maximumValue.Hour;
			}

			var hour = new RandomInteger(minHour, maxHour + 1).Next();

			var minMinute = 0;
			var maxMinute = 59;

			if (year == _minimumValue.Year && month == _minimumValue.Month && day == _minimumValue.Day
			    && hour == _minimumValue.Hour)
			{
				minMinute = _minimumValue.Minute;
			}

			if (year == _maximumValue.Year && month == _maximumValue.Month && day == _maximumValue.Day
			    && hour == _maximumValue.Hour)
			{
				maxMinute = _maximumValue.Minute;
			}

			var minute = new RandomInteger(minMinute, maxMinute + 1).Next();

			var minSecond = 0;
			var maxSecond = 59;

			if (year == _minimumValue.Year && month == _minimumValue.Month && day == _minimumValue.Day && hour == _minimumValue.Hour
			    && minute == _minimumValue.Month)
			{
				minSecond = _minimumValue.Second;
			}

			if (year == _maximumValue.Year && month == _maximumValue.Month && day == _maximumValue.Day && hour == _maximumValue.Hour
			    && minute == _maximumValue.Minute)
			{
				maxSecond = _maximumValue.Second;
			}

			var second = new RandomInteger(minSecond, maxSecond + 1).Next();

			return new DateTime(year, month, day, hour, minute, second);
		}
	}
}